namespace FileCollection
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Runtime.Remoting.Messaging;
    using System.Runtime.Serialization;

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T">Extra Information about the data</typeparam>
    public sealed class StreamFileQueue<T> : FileCollectionBase<Queue<Guid>, T>, IQueue
    {
        #region Delegates

        #endregion

        #region Constants
        /// private const int DefaultBufferSize = 0x1000; //4kb
        #endregion

        #region Constructor

        public StreamFileQueue(string directoryName, string queueName)
            : this(directoryName, queueName, false)
        {
        }

        public StreamFileQueue(string directoryPath, string name, bool automaticSave)
            : base(directoryPath, name, automaticSave)
        {
            CollectionType = "StreamFileQueue";
            Version = 1;
        }

        #endregion

        #region Finalizer

        ~StreamFileQueue()
        {
            Dispose(false);
        }

        #endregion

        #region Public Methods

        public void Flush()
        {
            ValidateException();
            SaveCollection();
        }

        public void Open()
        {
            Closed = false;
            ValidateException();
            LoadCollection();
        }

        public void Close()
        {
            lock (SyncRoot)
            {
                ValidateException();
                Closed = true;
                SaveCollection();
                Collection.Clear();
                Collection.TrimExcess();
            }
        }
      
        public bool DequeueToAnotherQueue(StreamFileQueue<T> targetQueue)
        {
            ValidateException();

            if (targetQueue == null)
            {
                throw new ArgumentNullException("targetQueue");
            }

            T info;
            Stream stream = this.Dequeue(out info);
            if (stream != null)
            {
                targetQueue.Enqueue(stream, info);
                return true;
            }

            return false;
        }

        public void Clear()
        {
            ValidateException();
            ///PARA MELHORAR O DESEMPENHO, COPIO A LISTA PARA UM ARRAY E DEPOIS LIMPO ASSIM MINIMIZANDO O TEMPO QUE O OBJETO FICA BLOQUEADO.
            Guid[] guids;

            lock (SyncRoot)
            {
                guids = Collection.ToArray();
                Collection.Clear();
            }

            SaveCollection();

            foreach (Guid id in guids)
            {
                File.Delete(GetItemFileName(id));
                File.Delete(GetItemInfoFileName(id));
            }
        }

        public Stream Dequeue(out T info)
        {
            ValidateException();

            while (Collection.Count > 0)
            {
                Guid id;
                lock (SyncRoot)
                {
                    id = Collection.Dequeue();
                }

                if (File.Exists(GetItemFileName(id)))
                {
                    try
                    {
                        LoadFileHandler<ExtraInformation<T>> loadInfo =
                            new LoadFileHandler<ExtraInformation<T>>(LoadInfoFile);

                        LoadFileHandler<FileStream> loadData = new LoadFileHandler<FileStream>(LoadDataFile);
                        IAsyncResult resultLoadData = loadData.BeginInvoke(id, true, null, null);
                        IAsyncResult resultLoadInfo = loadInfo.BeginInvoke(id, true, null, null);

                        if (AutomaticSave)
                        {
                            SaveCollection();
                        }

                        ExtraInformation<T> extraInfo = loadInfo.EndInvoke(resultLoadInfo);
                        info = extraInfo.ExtraInfo;

                        FileStream data;
                        data = loadData.EndInvoke(resultLoadData);

                        return data;
                    }
                    catch (SerializationException ex)
                    {
                        Trace.Fail(ex.Message, ex.ToString());
                    }
                    catch (IOException ex)
                    {
                        Trace.Fail(ex.Message, ex.ToString());
                    }
                }
            }
            
            info = default(T);
            return null;
        }

        /// <summary>
        ///  Enqueue a steram targetName Queue
        /// </summary>
        /// <param name="stream">stream data targetName enqueue</param>
        /// <param name="info">Extra Information about the stream data</param>
        /// <Date>16/05/2007</Date>
        /// <Author>Alan Santos Rodrigues</Author>
        public void Enqueue(Stream stream, T info)
        {
            ValidateException();
            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }

            Guid id = Guid.NewGuid();

            ///COMO AMBAS AS FUNCOES PRECISAM DO STREAM, NAO PODEM SER CONCORRENTES
            SaveInfoFile(id, GetExtraInfo(id, info, stream));
            SaveDataFile(id, stream);

            lock (SyncRoot)
            {
                Collection.Enqueue(id);
            }

            if (AutomaticSave)
            {
                SaveCollection();
            }
        }

        #endregion

        #region Private Methods

        protected override Header[] GetHeaders()
        {
            Header[] headers = new Header[3];
            headers[0] = new Header(HeaderCollectionName, Name, true, "urn:StreamFileQueue");
            headers[1] = new Header(HeaderVersion, Version, true, "urn:StreamFileQueue");
            headers[2] = new Header(HeaderCollectionType, CollectionType, true, "urn:StreamFileQueue");
            return headers;
        }
        
        protected override void CleanCollection()
        {
            Collection.Clear();
        }

        protected override void AddToCollection(Guid id)
        {
            Collection.Enqueue(id);
        }

        #endregion
    }
}
